package org.xlp.fraction;

import org.xlp.assertion.AssertUtils;
import org.xlp.utils.XLPVerifedUtil;
import org.xlp.utils.snumber.XLPMathUtil;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;

/**
 * @version 1.0
 * @author 徐龙平
 *         <p>
 *         2017-5-7
 *         </p>
 * 
 *         分数实现，功能主要有分数的四则运算，分数化简，主要针对小分子与分母组成的分数
 */
public class BaseFraction extends IFraction<BaseFraction, Long> {
	private static final long serialVersionUID = 936590011474600276L;

	/**
	 * 分子
	 */
	private long numerator;
	/**
	 * 分母
	 */
	private long denominator;

	// 值为0的分数常量
	public static final BaseFraction FRACTION_VALUE_OF_0 = new BaseFraction(
			0L, 1L);
	// 值为1的分数常量
	public static final BaseFraction FRACTION_VALUE_OF_1 = new BaseFraction(
			1L, 1L);
	// 值为-1的分数常量
	public static final BaseFraction FRACTION_VALUE_OF_NRGATE_1 = new BaseFraction(
			-1L, 1L);

	private BaseFraction() {}

	/**
	 * 以分子，分母构造分数对象
	 *
	 * @param numerator
	 *            分子
	 * @param denominator
	 *            分母
	 * @throws IllegalArgumentException
	 *             假如分母为0，抛出此异常
	 */
	public BaseFraction(long numerator, long denominator) {
		setNumerator(numerator);
		setDenominator(denominator);
	}

	/**
	 * 以分子，分母，计算结果是否以最简分数返回标志构造分数对象
	 *
	 * @param numerator
	 *            分子
	 * @param denominator
	 *            分母
	 * @throws IllegalArgumentException
	 *             假如分母为0，抛出此异常
	 */
	public BaseFraction(int numerator, int denominator) {
		this((long) numerator, denominator);
	}

	@Override
	public long longValue() {
		return numerator / denominator;
	}

	@Override
	public double doubleValue() {
		return (double)numerator / denominator ;
	}

	/**
	 * 设置分子
	 * 
	 * @param numerator
	 */
	protected void setNumerator(long numerator) {
		this.numerator = numerator;
	}

	/**
	 * 设置分母
	 * 
	 * @param denominator
	 * @throws IllegalArgumentException
	 *             假如分母为0，抛出此异常
	 */
	protected void setDenominator(long denominator) {
		if (denominator == 0)
			throw new IllegalArgumentException("分母不能为0");
		this.denominator = denominator;
		if (denominator < 0) {
			this.denominator = -denominator;
			this.numerator = -this.numerator;
		}
	}

	/**
	 * 判断分数值是否为1
	 *
	 * @return 是返回true，否则返回false
	 */
	public boolean isOne() {
		return this.denominator == this.numerator;
	}

	/**
	 * 判断分数值是否为0
	 *
	 * @return 是返回true，否则返回false
	 */
	public boolean isZero() {
		return this.numerator == 0;
	}

	/**
	 * 判断分数值是否为-1
	 *
	 * @return 是返回true，否则返回false
	 */
	public boolean isMinusOne() {
		return this.denominator == -this.numerator;
	}

	@Override
	public String toString() {
		if (this.numerator == 0)
			return "0";
		if (this.denominator == 1)
			return String.valueOf(this.numerator);

		return numerator + "/" + denominator;
	}

	/**
	 * 分数加法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	@Override
	public BaseFraction add(long fraction) {
	    numerator = fraction * denominator + numerator;
		return this;
	}

	/**
	 * 分数加法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	@Override
	public BaseFraction add(double fraction) {
		return add(valueOf(fraction));
	}

	/**
	 * 分数加法
	 *
	 * @param fraction
	 * @return 返回this
     * @throws NullPointerException 假如参数为null，则抛出该异常
	 */
	@Override
	public BaseFraction add(BaseFraction fraction) {
	    AssertUtils.isNotNull(fraction, "fraction parameter must be not null!");
        if (fraction.denominator == this.denominator) {
            numerator += fraction.getNumerator();
        } else {
            numerator = fraction.getDenominator() * numerator +
                    fraction.getNumerator() * this.denominator;
            setDenominator(denominator * fraction.getDenominator());
        }
		return this;
	}

	/**
	 * 分数减法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	@Override
	public BaseFraction subtract(long fraction) {
        numerator -= fraction * denominator;
		return this;
	}

	/**
	 * 分数减法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	@Override
	public BaseFraction subtract(double fraction) {
		return subtract(valueOf(fraction));
	}

	/**
	 * 分数减法
	 *
	 * @param fraction
	 * @return 返回this
     * @throws NullPointerException 假如参数为null，则抛出该异常
	 */
	@Override
	public BaseFraction subtract(BaseFraction fraction) {
        AssertUtils.isNotNull(fraction, "fraction parameter must be not null!");
        if (fraction.denominator == this.denominator) {
            numerator -= fraction.getNumerator();
        } else {
            numerator = fraction.getDenominator() * numerator -
                    fraction.getNumerator() * this.denominator;
            setDenominator(denominator * fraction.getDenominator());
        }
        return this;
	}

	/**
	 * 分数乘法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	@Override
	public BaseFraction multiply(long fraction) {
	    numerator *= fraction;
		return this;
	}

	/**
	 * 分数乘法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	@Override
	public BaseFraction multiply(double fraction) {
		return multiply(valueOf(fraction));
	}

	/**
	 * 分数乘法
	 *
	 * @param fraction
	 * @return 返回this
     * @throws NullPointerException 假如参数为null，则抛出该异常
	 */
	@Override
	public BaseFraction multiply(BaseFraction fraction) {
        AssertUtils.isNotNull(fraction, "fraction parameter must be not null!");
		numerator *= fraction.getNumerator();
        setDenominator(denominator * fraction.getDenominator());
        return this;
	}

	/**
	 * 分数除法
	 *
	 * @param fraction
	 * @return 返回this
     * @throws NullPointerException 假如参数为null，则抛出该异常
     * @throws IllegalArgumentException 假如除数为0，则抛出该异常
	 */
	@Override
	public BaseFraction divide(BaseFraction fraction) {
        AssertUtils.isNotNull(fraction, "fraction parameter must be not null!");
		if (fraction.isZero()) throw new IllegalArgumentException("除数不能为0！");
		numerator *= fraction.getDenominator();
        setDenominator(denominator * fraction.getNumerator());
        return this;
	}

	/**
	 * 分数除法
	 *
	 * @param fraction
	 * @return 返回this
     * @throws IllegalArgumentException 假如除数为0，则抛出该异常
	 */
	@Override
	public BaseFraction divide(long fraction) {
        setDenominator(denominator * fraction);
		return this;
	}

	/**
	 * 分数除法
	 *
	 * @param fraction
	 * @return 返回this
	 */
	@Override
	public BaseFraction divide(double fraction) {
		return divide(valueOf(fraction));
	}

	/**
	 * 取反
	 *
	 * @return 返回一个与this相反的分数新对象
	 */
	@Override
	public BaseFraction negate() {
		return new BaseFraction(-numerator, denominator);
	}

	/**
	 * 取绝对值
	 *
	 * @return 返回一个新的分数对象
	 */
	@Override
	public BaseFraction abs() {
		return new BaseFraction(Math.abs(numerator), denominator);
	}

	/**
	 * 计算this的n次幂
	 *
	 * @param n n次幂
	 * @return this的n次幂
     * @throws IllegalArgumentException 假如出现 0的0次幂或0的负数次幂，则抛出该异常
	 */
	@Override
	public BaseFraction pow(int n) {
	    boolean isZero = isZero();
        if (isZero && n <= 0) throw new IllegalArgumentException("0的0次幂或0的负数次幂无意义");
		if (isZero) {
		    numerator = 0;
		    denominator = 1;
        } else if(isOne() || n == 0 || (isMinusOne() && n % 2 == 0)){
			numerator = 1;
			denominator = 1;
		} else if(isMinusOne()){
			return this;
		} else if (n < 0){
			n = -n;
            numerator = (long) Math.pow(denominator, n);
            setDenominator((long) Math.pow(numerator, n));
        } else {
		    numerator = (long) Math.pow(numerator, n);
		    denominator = (long) Math.pow(denominator, n);
        }
        return this;
	}

	/**
	 * 得到最简分数
	 *
	 * @return 返回this
	 */
	@Override
	public BaseFraction getFraction() {
		long num = this.numerator < 0 ? -this.numerator : numerator;
		// 得到最大公约数
		long mbc = XLPMathUtil.getMaxCommonDivisor(num, this.denominator);
		return new BaseFraction(this.numerator / mbc, this.denominator / mbc);
	}

    /**
     * 比较分数对象的大小
     * @param fraction
     * @return -1： 被比较分数大，0：相等，1：本分数大
     * @throws NullPointerException 假如参数为null，则抛出该异常
     */
    @Override
    public int compareTo(IFraction fraction){
        if (fraction instanceof BaseFraction){
            BaseFraction f = (BaseFraction) fraction;
            if (getDenominator().equals(f.getDenominator())){
                return getNumerator().compareTo(f.getNumerator());
            }
            long result = getNumerator() * f.getDenominator() -
                    f.getNumerator() * getDenominator();
            return result == 0 ? 0 : result > 0 ? 1 : -1;
        }
        return super.compareTo(fraction);
    }

	/**
	 * 分数转换成指定的精度的小数BigDecimal
	 *
	 * @param accuracy 假如accuracy小于0，则重新设置为0
	 * @return
	 */
	@Override
	public BigDecimal decimalValue(int accuracy) {
		if (accuracy < 0){
			accuracy = 0;
		}
		return new BigDecimal(String.valueOf(numerator)).divide(new BigDecimal(
				String.valueOf(denominator)), accuracy, RoundingMode.HALF_UP);
	}

	/**
	 * 分数转换成小数BigDecimal
	 *
	 * @return
	 */
	@Override
	public BigDecimal decimalValue() {
		return new BigDecimal(String.valueOf(doubleValue()));
	}

	/**
	 * 分数转换成BigInteger
	 *
	 * @return
	 */
	@Override
	public BigInteger bigIntegerValue() {
		return new BigInteger(String.valueOf(longValue()));
	}

	/**
	 * 获取分数的分子(可能返回的是转换后的分子)
	 *
	 * @return
	 */
	@Override
	public Long getNumerator() {
		return numerator;
	}

	/**
	 * 获取分数的分母(可能返回的是转换后的分母)
	 *
	 * @return
	 */
	@Override
	public Long getDenominator() {
		return denominator;
	}

    /**
     * 用字符串数组构造分数对象，数组存储的是小数的整数部分与小数部分
     *
     * @param num
     * @return
     */
    private static BaseFraction valueOf0(String[] num) {
        BaseFraction fraction = new BaseFraction();
        if (num.length == 1 || num[1].matches(ZERO_REGEX)){
            fraction.setNumerator(Long.parseLong(num[0].trim()));
            fraction.setDenominator(1L);
        } else {
            String dec = num[1];
            int len = dec.length();
            if ("".equals(num[0])) {
                num[0] = "0";
            }
            long des1 = Long.parseLong(num[0]);
            long mis1 = Long.parseLong(dec);
            fraction.setNumerator((long) Math.pow(10, len) * des1 + mis1);
            fraction.setDenominator((long) Math.pow(10, len));
        }
        return fraction;
    }

    /**
     * 把浮点数转换成分数对象
     *
     * @param num
     * @return
     */
    public static BaseFraction valueOf(double num) {
        String[] split = String.valueOf(num).split("\\.");
        return valueOf0(split);
    }

    /**
     * 把浮点数转换成分数对象
     *
     * @param num
     * @return
     * @throws IllegalArgumentException
     *             假如参数为null，抛出此异常
     */
    public static BaseFraction valueOf(BigDecimal num) {
        AssertUtils.isNotNull(num, "mum parameter must be not null!");
        String[] split = num.toPlainString().split("\\.");
        return valueOf0(split);
    }

    /**
     * 把分数字符串转换成分数对象
     *
     * @param fraction
     * @return
     * @throws IllegalArgumentException
     *             假如参数格式错误，抛出该异常
     * @throws NullPointerException
     * 			 假如参数为null，则抛出该异常
     */
    public static BaseFraction valueOf(String fraction) {
        AssertUtils.isNotNull(fraction, "fraction parameter must be not null!");
        fraction = fraction.trim();
        if (!XLPVerifedUtil.isFraction(fraction)) {
            throw new IllegalArgumentException("参数格式错误，param:[" + fraction + "]");
        }

        String[] rs = fraction.split("[/]");
        if (rs.length >= 2 && rs[1].matches(ZERO_REGEX)) {
            throw new IllegalArgumentException("参数格式错误，param:[" + fraction + "]");
        }
        return valueOf0(rs);
    }

	/**
	 * 使用小数字符串创建分数对象
	 * @param decimal
	 * @return
	 * @throws NullPointerException 假如参数为null或空，则抛出该异常
	 * @throws IllegalArgumentException 假如参数解析不了则抛出该异常
	 */
	public static BaseFraction fromDecimalStr(String decimal){
		AssertUtils.isNotNull(decimal, "decimal parameter must be not null or empty!");
		if (!XLPVerifedUtil.isNumber(decimal)){
			throw new IllegalArgumentException(decimal + "：不是合法的实数字符串");
		}
		String[] split = decimal.split("\\.");
		return valueOf0(split);
	}
}
